home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
AmigActive 10
/
AACD 10.iso
/
AACD
/
Games
/
MAME
/
src
/
msdos
/
nec765.c
< prev
next >
Wrap
C/C++ Source or Header
|
1999-09-02
|
32KB
|
1,093 lines
/*****************************************************************************
*
* nec765.c
*
* allow direct access to a PCs NEC 765 floppy disc controller
*
* !!!! this is ALPHA - be careful !!!!
*
*****************************************************************************/
#include <go32.h>
#include <pc.h>
#include <dpmi.h>
#include <time.h>
#include "driver.h"
#include "mess/machine/wd179x.h"
#define RE_INTERLEAVE 0
#define VERBOSE 1
/* since most PC floppy disc controllers don't really support FM, */
/* set this to 0x40 to always format/read/write in MFM mode */
#define MFM_OVERRIDE 0x40
#if VERBOSE
#define LOG(msg) if (log) fprintf msg
#else
#define LOG(msg)
#endif
typedef struct
{
unsigned drive_0:1; /* drive 0 in seek mode / busy */
unsigned drive_1:1; /* drive 1 in seek mode / busy */
unsigned drive_2:1; /* drive 2 in seek mode / busy */
unsigned drive_3:1; /* drive 3 in seek mode / busy */
unsigned io_active:1; /* read or write command in progress */
unsigned non_dma_mode:1; /* fdc is in non DMA mode if set */
unsigned read_from_fdc:1; /* request for master (1 fdc->cpu, 0 cpu->fdc) */
unsigned ready:1; /* data reg ready for i/o */
} STM;
typedef struct
{
unsigned unit_select:2; /* unit select */
unsigned head:1; /* head */
unsigned not_ready:1; /* not ready */
unsigned equip_check:1; /* equipment check (drive fault) */
unsigned seek_end:1; /* seek end */
unsigned int_code:2; /* interrupt code (0: ok, 1:abort, 2:ill cmd, 3:ready chg) */
} ST0;
typedef struct
{
unsigned missing_AM:1; /* missing address mark */
unsigned not_writeable:1; /* not writeable */
unsigned no_data:1; /* no data */
unsigned x3:1; /* always 0 */
unsigned overrun:1; /* overrun */
unsigned data_error:1; /* data error */
unsigned x6:1; /* always 0 */
unsigned end_of_cylinder:2; /* end of cylinder */
} ST1;
typedef struct
{
unsigned missing_DAM:1; /* missing (deleted) data address mark */
unsigned bad_cylinder:1; /* bad cylinder (cylinder == 255) */
unsigned scan_not_satisfied:1; /* scan not satisfied */
unsigned scan_equal_hit:1; /* scan equal hit */
unsigned wrong_cylinder:1; /* wrong cylinder (mismatch) */
unsigned data_error:1; /* data error */
unsigned control_mark:1; /* control mark (deleted data address mark) */
unsigned x7:1; /* always 0 */
} ST2;
typedef struct
{
unsigned unit_select:2; /* unit select */
unsigned head:1; /* head */
unsigned two_side:1; /* two side */
unsigned track_0:1; /* track 0 (not reliable!?) */
unsigned ready:1; /* ready */
unsigned write_protect:1; /* write protect */
unsigned fault:1; /* fault */
} ST3;
typedef struct
{
ST0 st0; /* status 0 */
ST1 st1; /* status 1 */
ST2 st2; /* status 2 */
UINT8 c; /* cylinder */
UINT8 h; /* head */
UINT8 s; /* sector */
UINT8 n; /* sector length */
ST3 st3; /* status 3 (after sense drive status cmd) */
UINT8 pcn;
} STA;
typedef struct
{
UINT8 unit; /* unit number 0 = A:, 1 = B: */
UINT8 type; /* type of drive 0: 360K, 1: 720K, 2: 1.2M, 3: 1.44M */
UINT8 eot; /* end of track */
UINT8 secl; /* sector length code (0:128, 1:256, 2:512 ... ) */
UINT8 gap_a; /* read/write gap A*/
UINT8 gap_b; /* format gap B */
UINT8 dtl; /* data length (128 if secl == 0) */
UINT8 filler; /* format filler UINT8 (E5) */
UINT8 mfm; /* 0x00 = FM, 0x40 = MFM */
UINT8 rcmd; /* read command (0x26 normal, 0x2C deleted data address mark) */
UINT8 wcmd; /* write command (0x05 normal, 0x09 deleted data address mark) */
UINT8 dstep; /* double step shift factor (1 for 40 track disk in 80 track drive) */
STM stm; /* main status */
STA sta; /* status file */
} NEC;
typedef struct {
UINT8 density;
UINT8 spt;
UINT8 secl;
UINT8 gap_a;
UINT8 gap_b;
} GAPS;
static GAPS gaps[] = {
{DEN_FM_LO, 1, 4, 200, 255},
{DEN_FM_LO, 2, 3, 200, 255},
{DEN_FM_LO, 4, 2, 70, 135},
{DEN_FM_LO, 5, 2, 56, 98},
{DEN_FM_LO, 5, 1, 200, 255},
{DEN_FM_LO, 6, 1, 70, 135},
{DEN_FM_LO, 7, 1, 56, 98},
{DEN_FM_LO, 8, 1, 25, 48},
{DEN_FM_LO, 9, 1, 7, 9},
{DEN_FM_LO, 16, 0, 16, 25},
{DEN_FM_LO, 17, 0, 11, 17},
{DEN_FM_LO, 18, 0, 7, 9},
{DEN_FM_HI, 2, 4, 200, 255},
{DEN_FM_HI, 3, 4, 42, 80},
{DEN_FM_HI, 4, 3, 128, 240},
{DEN_FM_HI, 5, 3, 70, 135},
{DEN_FM_HI, 8, 2, 42, 80},
{DEN_FM_HI, 9, 2, 42, 80},
{DEN_FM_HI, 10, 2, 10, 12},
{DEN_FM_HI, 16, 1, 32, 50},
{DEN_FM_HI, 18, 1, 10, 12},
{DEN_FM_HI, 26, 0, 7, 9},
{DEN_MFM_LO, 1, 5, 200, 255},
{DEN_MFM_LO, 2, 4, 200, 255},
{DEN_MFM_LO, 3, 4, 26, 46},
{DEN_MFM_LO, 4, 3, 128, 240},
{DEN_MFM_LO, 5, 3, 21, 31},
{DEN_MFM_LO, 8, 2, 42, 80},
{DEN_MFM_LO, 9, 2, 26, 46},
{DEN_MFM_LO, 10, 2, 10, 12},
{DEN_MFM_LO, 16, 1, 32, 50},
{DEN_MFM_LO, 17, 1, 21, 31},
{DEN_MFM_LO, 18, 1, 10, 12},
{DEN_MFM_LO, 19, 1, 7, 9},
{DEN_MFM_HI, 2, 5, 200, 255},
{DEN_MFM_HI, 4, 4, 200, 255},
{DEN_MFM_HI, 5, 4, 42, 80},
{DEN_MFM_HI, 8, 3, 128, 240},
{DEN_MFM_HI, 9, 3, 76, 148},
{DEN_MFM_HI, 10, 3, 32, 50},
{DEN_MFM_HI, 11, 3, 10, 12},
{DEN_MFM_HI, 16, 2, 128, 240},
{DEN_MFM_HI, 17, 2, 70, 135},
{DEN_MFM_HI, 18, 2, 42, 80},
{DEN_MFM_HI, 19, 2, 32, 50},
{DEN_MFM_HI, 32, 1, 128, 240},
{DEN_MFM_HI, 33, 1, 42, 80},
{DEN_MFM_HI, 34, 1, 32, 50},
{DEN_MFM_HI, 35, 1, 21, 31},
{DEN_MFM_HI, 36, 1, 10, 12},
{0,0,0,0,0}
};
static FILE *log = NULL;
static NEC nec;
static UINT8 unit_type[4] = {0xff, 0xff, 0xff, 0xff};
static __dpmi_paddr old_irq_E; /* previous interrupt vector 0E */
static __dpmi_paddr new_irq_E; /* new interrupt vector 0E */
static _go32_dpmi_seginfo nec_dma; /* for allocation of a sector DMA buffer */
static UINT8 irq_flag = 0; /* set by IRQ 6 (INT 0E) */
static UINT8 initialized = 0;
static void *timer_motors = NULL;
/*****************************************************************************
* convert NEC 765 status to WD179X status 1 (restore, seek, step)
*****************************************************************************/
static UINT8 FDC_STA1(void)
{
UINT8 sta = 0;
#if 0 /* since the track_0 status seems not to be reliable */
if (!nec.sta.st3.track_0) /* track 0 */
sta |= STA_1_TRACK0;
#else /* I did it that way */
if (!nec.sta.pcn == 0) /* cylinder number 0 */
sta |= STA_1_TRACK0;
#endif
if (nec.sta.st3.write_protect) /* write protect */
sta |= STA_1_WRITE_PRO;
if (!nec.sta.st3.ready) /* not ready */
sta |= STA_1_NOT_READY;
return sta;
}
/*****************************************************************************
* convert NEC 765 status to WD179X status 2 (read, write, format)
*****************************************************************************/
static UINT8 FDC_STA2(int read_mode)
{
UINT8 sta = 0;
if (nec.sta.st1.overrun) /* overrun ? */
sta |= STA_2_LOST_DAT;
if (nec.sta.st1.data_error || /* data error (DAM) ? */
nec.sta.st2.data_error) /* data error (SEC) ? */
sta |= STA_2_CRC_ERR;
if (nec.sta.st1.no_data || /* no data ? */
nec.sta.st1.missing_AM) /* missing address mark ? */
sta |= STA_2_REC_N_FND;
if (nec.sta.st2.control_mark) /* control mark ? */
sta |= STA_2_REC_TYPE;
if (nec.sta.st1.not_writeable) /* not writeable ? */
sta |= STA_2_WRITE_PRO;
if (nec.sta.st0.int_code == 3) /* interrupt code == ready change ? */
sta |= STA_2_NOT_READY;
if (read_mode && !(sta & ~STA_2_REC_TYPE))
sta |= STA_2_DRQ | STA_2_BUSY;
return sta;
}
/*****************************************************************************
* initialize dma controller to read count bytes to ofs
*****************************************************************************/
static void dma_read(int count)
{
int ofs = nec_dma.rm_segment * 16 + nec_dma.rm_offset;
LOG((log,"NEC765 dma_read %08X %04X\n", ofs, count));
outportb(0x0a, 0x04 | 0x02); /* mask DMA channel 2 */
outportb(0x0b, 0x46); /* DMA read command */
outportb(0x0c, 0); /* reset first/last flipflop */
outportb(0x05, (count - 1) & 0xff); /* transfer size low */
outportb(0x05, (count - 1) >> 8); /* transfer size high */
outportb(0x04, ofs & 0xff); /* transfer offset low */
outportb(0x04, (ofs >> 8) & 0xff); /* transfer offset high */
outportb(0x81, (ofs >> 16) & 0xff); /* select 64k page */
outportb(0x0a, 0x02); /* start DMA channel 2 */
}
/*****************************************************************************
* initialize dma controller to read count bytes to ofs
*****************************************************************************/
static void dma_write(int count)
{
int ofs = nec_dma.rm_segment * 16 + nec_dma.rm_offset;
LOG((log,"NEC765 dma_write %08X %04X\n", ofs, count));
outportb(0x0a, 0x04 | 0x02); /* mask DMA channel 2 */
outportb(0x0b, 0x4a); /* DMA write command */
outportb(0x0c, 0); /* reset first/last flipflop */
outportb(0x05, (count - 1) & 0xff); /* transfer size low */
outportb(0x05, (count - 1) >> 8); /* transfer size high */
outportb(0x04, ofs & 0xff); /* transfer offset low */
outportb(0x04, (ofs >> 8) & 0xff); /* transfer offset high */
outportb(0x81, (ofs >> 16) & 0xff); /* select 64k page */
outportb(0x0a, 0x02); /* start DMA channel 2 */
}
/*****************************************************************************
* read NECs main status
*****************************************************************************/
static int main_status(void)
{
uclock_t timeout;
uclock_t utime = uclock();
timeout = utime + UCLOCKS_PER_SEC;
while (utime < timeout)
{
*(UINT8 *)&nec.stm = inportb(0x3f4);
if (nec.stm.ready)
break;
utime = uclock();
}
timeout -= uclock();
if (timeout <= 0)
{
LOG((log,"NEC765 STM: %02X RDY%c DIR%c IO%c NDMA%c %c%c%c%c\n",
*(UINT8 *) &nec.stm,
(nec.stm.ready) ? '*' : '-',
(nec.stm.read_from_fdc) ? '-' : '+',
(nec.stm.io_active) ? '*' : '-',
(nec.stm.non_dma_mode) ? '*' : '-',
(nec.stm.drive_0) ? 'A' : '*',
(nec.stm.drive_1) ? 'B' : '*',
(nec.stm.drive_2) ? 'C' : '*',
(nec.stm.drive_3) ? 'D' : '*'));
}
return (timeout > 0);
}
/*****************************************************************************
* NECs results phase: read everything into the status registers
*****************************************************************************/
static void results(int sense_interrupt)
{
if (sense_interrupt)
{
if (main_status() && nec.stm.read_from_fdc)
{
*(UINT8*)&nec.sta.st3 = inportb(0x3F5);
LOG((log,"NEC765 ST3: 0x%02X US%d HD%d TS%c T0%c RY%c WP%c FT%c\n",
*(UINT8*)&nec.sta.st3,
nec.sta.st3.unit_select,
nec.sta.st3.head,
(nec.sta.st3.two_side) ? '*' : '-',
(nec.sta.st3.track_0) ? '*' : '-',
(nec.sta.st3.ready) ? '*' : '-',
(nec.sta.st3.write_protect) ? '*' : '-',
(nec.sta.st3.fault) ? '*' : '-'));
}
if (main_status() && nec.stm.read_from_fdc)
{
*(UINT8*)&nec.sta.pcn = inportb(0x3F5);
LOG((log,"NEC765 PCN: %02d\n", nec.sta.pcn));
}
}
else
{
if (main_status() && nec.stm.read_from_fdc)
{
*(UINT8*)&nec.sta.st0 = inportb(0x3F5);
LOG((log,"NEC765 ST0: 0x%02X US%d HD%d NR%c EC%c SE%c IC%d\n",
*(UINT8*)&nec.sta.st0,
nec.sta.st0.unit_select,
nec.sta.st0.head,
(nec.sta.st0.not_ready) ? '*' : '-',
(nec.sta.st0.equip_check) ? '*' : '-',
(nec.sta.st0.seek_end) ? '*' : '-',
nec.sta.st0.int_code));
}
if (main_status() && nec.stm.read_from_fdc)
{
*(UINT8*)&nec.sta.st1 = inportb(0x3F5);
LOG((log,"NEC765 ST1: 0x%02X MA%c NW%c ND%c OR%c DE%c EC%c\n",
*(UINT8*)&nec.sta.st1,
(nec.sta.st1.missing_AM) ? '*' : '-',
(nec.sta.st1.not_writeable) ? '*' : '-',
(nec.sta.st1.no_data) ? '*' : '-',
(nec.sta.st1.overrun) ? '*' : '-',
(nec.sta.st1.data_error) ? '*' : '-',
(nec.sta.st1.end_of_cylinder) ? '*' : '-'));
}
if (main_status() && nec.stm.read_from_fdc)
{
*(UINT8*)&nec.sta.st2 = inportb(0x3F5);
LOG((log,"NEC765 ST2: 0x%02X MD%c BC%c SN%c SH%c WC%c DD%c CM%c\n",
*(UINT8*)&nec.sta.st2,
(nec.sta.st2.missing_DAM) ? '*' : '-',
(nec.sta.st2.bad_cylinder) ? '*' : '-',
(nec.sta.st2.scan_not_satisfied) ? '*' : '-',
(nec.sta.st2.scan_equal_hit) ? '*' : '-',
(nec.sta.st2.wrong_cylinder) ? '*' : '-',
(nec.sta.st2.data_error) ? '*' : '-',
(nec.sta.st2.control_mark) ? '*' : '-'));
}
if (main_status() && nec.stm.read_from_fdc)
{
*(UINT8*)&nec.sta.c = inportb(0x3F5);
LOG((log,"NEC765 C:%02d", nec.sta.c));
}
if (main_status() && nec.stm.read_from_fdc)
{
*(UINT8*)&nec.sta.h = inportb(0x3F5);
LOG((log," H:%d", nec.sta.h));
}
if (main_status() && nec.stm.read_from_fdc)
{
*(UINT8*)&nec.sta.s = inportb(0x3F5);
LOG((log," S:%02d", nec.sta.s));
}
if (main_status() && nec.stm.read_from_fdc)
{
*(UINT8*)&nec.sta.n = inportb(0x3F5);
LOG((log," N:%2d", nec.sta.n));
}
LOG((log,"\n"));
}
/* more data? shouldn't happen, but read it away */
while (main_status() && nec.stm.read_from_fdc)
{
UINT8 data = inportb(0x3F5);
LOG((log,"NEC765 read unexpected %02X\n", data));
}
}
/*****************************************************************************
* out_nec: send a UINT8 to the NEC
*****************************************************************************/
static int out_nec(UINT8 b)
{
int result = 1;
if (main_status())
{
if (nec.stm.read_from_fdc)
{
LOG((log,"NEC765 read_from_fdc!\n"));
results(0);
}
else
{
LOG((log,"0x%02X ", b));
outportb(0x3f5, b);
result = 0;
}
}
return result;
}
/*****************************************************************************
* seek_exec: wait until seek is executed and sense interrupt status
*****************************************************************************/
static void seek_exec(UINT8 * track)
{
uclock_t timeout;
uclock_t utime = uclock();
timeout = utime + UCLOCKS_PER_SEC * 5;
while (utime < timeout)
{
main_status();
if (irq_flag)
break;
utime = uclock();
}
timeout -= uclock();
if (nec.stm.read_from_fdc)
results(0);
LOG((log,"NEC765 command "));
if (out_nec(0x08)) /* sense interrupt status */
return;
LOG((log,"\n"));
results(1);
if (track)
*track = nec.sta.pcn;
}
/*****************************************************************************
* specify drive parameters
* step rate, head unload timing, head load timing, DMA mode
*****************************************************************************/
static void specify(void)
{
uclock_t timeout;
uclock_t utime = uclock();
LOG((log,"NEC765 specify\n"));
timeout = utime + UCLOCKS_PER_SEC;
irq_flag = 0;
outportb(0x3f2, 0x08); /* reset fdc */
while (utime < timeout)
{
if (irq_flag)
break;
utime = uclock();
}
osd_fdc_motors(nec.unit); /* take away reset */
LOG((log,"NEC765 command "));
if (out_nec(0x03)) /* specify command */
return;
if (out_nec(0xdf)) /* steprate, head unload timing */
return;
if (out_nec(0x04)) /* head load timing, DMA mode */
return;
LOG((log,"\n"));
}
/*****************************************************************************
* read_sector:
* read a sector number 'sector' from track 'track', side 'side' using head
* number 'head' of the floppy disk. if the first try fails, change the
* DDAM mode and try again
*****************************************************************************/
static UINT8 read_sector(UINT8 track, UINT8 side, UINT8 head, UINT8 sector)
{
int try;
LOG((log,"NEC765 read unit:%d track:%02d side:%d head:%d sector:%d\n",
nec.unit, track, side, head, sector));
/* three tries to detect normal / deleted data address mark */
for (try = 0; try < 3; try++)
{
dma_read((nec.secl) ? nec.secl << 8 : nec.dtl);
irq_flag = 0;
LOG((log,"NEC765 command "));
if (out_nec(nec.mfm | nec.rcmd)) /* read sector */
return STA_2_NOT_READY;
if (out_nec((side << 2) + nec.unit)) /* side * 4 + unit */
return STA_2_NOT_READY;
if (out_nec(track)) /* track */
return STA_2_NOT_READY;
if (out_nec(head)) /* head (can be 0 for side 1!) */
return STA_2_NOT_READY;
if (out_nec(sector)) /* sector */
return STA_2_NOT_READY;
if (out_nec(nec.secl)) /* bytes per sector */
return STA_2_NOT_READY;
if (out_nec(nec.eot)) /* end of track */
return STA_2_NOT_READY;
if (out_nec(nec.gap_a)) /* gap A */
return STA_2_NOT_READY;
if (out_nec(nec.dtl)) /* data length */
return STA_2_NOT_READY;
LOG((log,"\n"));
for ( ; ; )
{
main_status();
if (irq_flag)
break;
// if (!nec.stm.io_active)
// break;
}
results(0);
if (!nec.sta.st2.control_mark)
break;
/* if control mark is set, toggle DDAM mode and try again */
nec.rcmd ^= 0x0A; /* toggle between 0x26 and 0x2C */
}
nec.sta.st2.control_mark = 0;
/* set status 2 control_mark if read deleted was successful */
if (nec.rcmd & 0x08)
{
LOG((log,"NEC765 read *DDAM*\n"));
nec.sta.st2.control_mark = 1;
nec.rcmd ^= 0x0A; /* toggle back */
}
return FDC_STA2(1);
}
/*****************************************************************************
* write_sector:
* write sector number 'sector' to track 'track', side 'side' of
* the floppy disk using head number 'head'. if ddam in non zero,
* write the sector with deleted data address mark (DDAM)
*****************************************************************************/
static UINT8 write_sector(UINT8 track, UINT8 side, UINT8 head, UINT8 sector, UINT8 ddam)
{
int i;
LOG((log,"NEC765 write unit:%d track:%02d side:%d head:%d sector:%d%s\n",
nec.unit, track, side, head, sector, (ddam) ? " *DDAM*" : ""));
irq_flag = 0;
dma_write((nec.secl) ? nec.secl << 8 : nec.dtl);
nec.wcmd = (ddam) ? 0x09 : 0x05;
LOG((log,"NEC765 command "));
if (out_nec(nec.mfm | nec.wcmd)) /* write sector */
return STA_2_NOT_READY;
if (out_nec((side << 2) + nec.unit)) /* side * 4 + unit */
return STA_2_NOT_READY;
if (out_nec(track)) /* track */
return STA_2_NOT_READY;
if (out_nec(head)) /* head (can be 0 for side 1!) */
return STA_2_NOT_READY;
if (out_nec(sector)) /* sector */
return STA_2_NOT_READY;
if (out_nec(nec.secl)) /* bytes per sector */
return STA_2_NOT_READY;
if (out_nec(nec.eot)) /* end of track */
return STA_2_NOT_READY;
if (out_nec(nec.gap_a)) /* gap_a */
return STA_2_NOT_READY;
if (out_nec(nec.dtl)) /* data length */
return STA_2_NOT_READY;
LOG((log,"\n"));
for (i = 0; i < 3; i++)
{
main_status();
if (!nec.stm.io_active)
break;
if (irq_flag)
break;
}
results(0);
return FDC_STA2(0);
}
/*****************************************************************************
* real mode interrupt 0x0E
*****************************************************************************/
static void irq_E(void)
{
asm("nop \n" /* "nop" to find interrupt entry */
"sti \n" /* enable interrupts */
"pushl %eax \n" /* save EAX */
"pushw %ds \n" /* save DS */
"movw $0x0000, %ax \n" /* patched with _my_ds() */
"movw %ax, %ds \n" /* transfer to DS */
"orb $0x80, _irq_flag \n" /* set IRQ occured flag */
"popw %ds \n" /* get back DS */
"movb $0x20, %al \n" /* end of interrupt */
"outb %al, $0x20 \n" /* to PIC */
"popl %eax \n" /* get back EAX */
"iret \n");
}
int osd_fdc_init(void)
{
__dpmi_meminfo meminfo;
unsigned long _my_cs_base;
unsigned long _my_ds_base;
UINT8 *p;
if (initialized)
return 1;
log = fopen("nec.log", "w");
for (p = (UINT8 *) & irq_E; p; p++)
{
/* found "nop" ? */
if (p[0] == 0x90)
new_irq_E.offset32 = (int) p + 1;
/* found "movw $0000,%ax" ? */
if ((p[0] == 0xb8) && (p[1] == 0x00) && (p[2] == 0x00))
{
p++;
*(short *)p = _my_ds();
break;
}
}
__dpmi_get_segment_base_address(_my_cs(), &_my_cs_base);
__dpmi_get_segment_base_address(_my_ds(), &_my_ds_base);
__dpmi_get_protected_mode_interrupt_vector(0x0E, &old_irq_E);
meminfo.handle = _my_cs();
meminfo.size = 128; /* should be enough */
meminfo.address = _my_cs_base + (unsigned long) &irq_E;
if (__dpmi_lock_linear_region(&meminfo))
{
LOG((log,"NEC765 init: could not lock code %lx addr:%08lx size:%ld\n",
meminfo.handle, meminfo.address, meminfo.size));
return 0;
}
meminfo.handle = _my_ds();
meminfo.size = 128; /* should be enough */
meminfo.address = _my_ds_base + (unsigned long) &old_irq_E;
if (__dpmi_lock_linear_region(&meminfo))
{
LOG((log,"NEC765 init: could not lock data\n"));
return 0;
}
nec_dma.size = 4096; /* request a 4K buffer for NEC DMA */
if (_go32_dpmi_allocate_dos_memory(&nec_dma))
{
LOG((log,"NEC765 init: could not alloc DOS memory size:%ld\n", nec_dma.size));
return 0;
}
new_irq_E.selector = _my_cs();
if (__dpmi_set_protected_mode_interrupt_vector(0x0E, &new_irq_E))
{
LOG((log,"NEC765 init: could not set vector 0x0E"));
return 0;
}
initialized = 1;
atexit(osd_fdc_exit);
specify();
return 1;
}
void osd_fdc_exit(void)
{
__dpmi_meminfo meminfo;
unsigned long _my_cs_base;
unsigned long _my_ds_base;
if (!initialized)
return;
outportb(0x3f2, 0x0c);
if (log)
fclose(log);
__dpmi_get_segment_base_address(_my_cs(), &_my_cs_base);
__dpmi_get_segment_base_address(_my_ds(), &_my_ds_base);
__dpmi_set_protected_mode_interrupt_vector(0x0E, &old_irq_E);
meminfo.handle = _my_cs();
meminfo.size = 128; /* should be enough */
meminfo.address = _my_cs_base + (unsigned long) &irq_E;
__dpmi_unlock_linear_region(&meminfo);
meminfo.handle = _my_ds();
meminfo.size = 128; /* should be enough */
meminfo.address = _my_ds_base + (unsigned long) &old_irq_E;
__dpmi_unlock_linear_region(&meminfo);
if (nec_dma.rm_segment)
_go32_dpmi_free_dos_memory(&nec_dma);
if (log)
fclose(log);
log = NULL;
initialized = 0;
}
void osd_fdc_motors(UINT8 unit)
{
nec.unit = unit;
switch (nec.unit)
{
case 0: /* drive 0 */
if (!timer_motors)
LOG((log, "FDD A: start\n"));
outportb(0x3f2, 0x1c);
break;
case 1: /* drive 1 */
if (!timer_motors)
LOG((log, "FDD B: start\n"));
outportb(0x3f2, 0x2d);
break;
}
if (timer_motors)
timer_remove(timer_motors);
timer_motors = timer_set(10.0, nec.unit, osd_fdc_interrupt);
}
/*****************************************************************************
* specify density, sectors per track and sector length
*****************************************************************************/
void osd_fdc_density(UINT8 unit, UINT8 density, UINT8 tracks, UINT8 spt, UINT8 eot, UINT8 secl)
{
__dpmi_regs r = {{0,}, };
int i;
nec.unit = unit;
nec.eot = eot;
nec.secl = secl;
nec.dstep = 0;
/* BIOS get current drive parameters */
r.x.ax = 0x0800;
r.h.dl = nec.unit;
__dpmi_int(0x13, &r);
nec.type = r.h.bl;
if (nec.type != unit_type[nec.unit])
{
switch (nec.type)
{
case 1: /* 5.25" 360K */
LOG((log,"NEC765 FDD %c: type 5.25\" 360K, sides %d, DOS spt %d%s\n",
'A' + nec.unit, r.h.dh + 1, r.h.cl & 0x1f, (nec.dstep) ? " (double steps)":""));
r.x.ax = 0x1701;
break;
case 2: /* 5.25" 1.2M */
if (tracks < 50)
nec.dstep = 1;
LOG((log,"NEC765 FDD %c: type 5.25\" 1.2M, sides %d, DOS spt %d%s\n",
'A' + nec.unit, r.h.dh + 1, r.h.cl & 0x1f, (nec.dstep) ? " (double steps)":""));
r.x.ax = 0x1703;
break;
case 3: /* 3.5" 720K */
if (tracks < 50)
nec.dstep = 1;
LOG((log,"NEC765 FDD %c: type 3.5\" 720K, sides %d, DOS spt %d%s\n",
'A' + nec.unit, r.h.dh + 1, r.h.cl & 0x1f, (nec.dstep) ? " (double steps)":""));
r.x.ax = 0x1704;
break;
case 4: /* 3.5" 1.44M */
if (tracks < 50)
nec.dstep = 1;
LOG((log,"NEC765 FDD %c: type 3.5\" 1.44M, sides %d, DOS spt %d%s\n",
'A' + nec.unit, r.h.dh + 1, r.h.cl & 0x1f, (nec.dstep) ? " (double steps)":""));
r.x.ax = 0x1703;
break;
default: /* unknown */
LOG((log,"NEC765 FDD %c: unknown type! sides %d, DOS spt %d\n",
'A' + nec.unit, r.h.dh + 1, r.h.cl & 0x1f));
r.x.ax = 0x1703;
break;
}
unit_type[nec.unit] = nec.type;
__dpmi_int(0x13, &r);
for (i = 0; gaps[i].spt; i++)
{
if (gaps[i].density == density && gaps[i].secl == secl && spt <= gaps[i].spt)
{
nec.gap_a = gaps[i].gap_a;
nec.gap_b = gaps[i].gap_b;
break;
}
}
if (gaps[i].spt)
{
LOG((log,"NEC765 using gaps entry #%d {%d,%d,%d,%d,%d} for %d,%d,%d\n",
i,gaps[i].density,gaps[i].spt,gaps[i].secl,gaps[i].gap_a,gaps[i].gap_b,density,spt,secl));
}
else
{
LOG((log,"NEC765 ERR den:%d spt:%d secl:%d not found!\n",density,spt,secl));
nec.gap_a = 10;
nec.gap_b = 12;
}
nec.dtl = (secl)?0xff:0x80; /* data length */
nec.filler = 0xe5; /* format filler UINT8 */
LOG((log,"NEC765 FDD %c: den:%d trks:%d spt:%d eot:%d secl:%d gap_a:%d gap_b:%d\n",
'A' + nec.unit, density, tracks, spt, eot, secl, nec.gap_a, nec.gap_b));
nec.rcmd = 0x26; /* read command (0x26 normal, 0x2C deleted data address mark) */
nec.wcmd = 0x05; /* write command (0x05 normal, 0x09 deleted data address mark) */
}
switch (density)
{
case DEN_FM_LO:
nec.mfm = MFM_OVERRIDE; /* FM commands */
outportb(0x3f7, 0x02); /* how do we set 125 kbps ?? */
break;
case DEN_FM_HI:
nec.mfm = MFM_OVERRIDE; /* FM commands */
outportb(0x3f7, 0x02); /* set 250/300 kbps */
break;
case DEN_MFM_LO:
nec.mfm = 0x40; /* MFM commands */
outportb(0x3f7, 0x02); /* set 250/300 kbps */
break;
case DEN_MFM_HI:
nec.mfm = 0x40; /* MFM commands */
outportb(0x3f7, 0x01); /* set 500 kbps */
break;
}
}
void osd_fdc_interrupt(int param)
{
timer_motors = NULL;
/* stop the motor(s) */
LOG((log,"NEC765 FDD %c: stop", 'A' + param));
outportb(0x3f2, 0x0c);
}
UINT8 osd_fdc_recal(UINT8 * track)
{
LOG((log,"NEC765 recal\n"));
irq_flag = 0;
LOG((log,"NEC765 command "));
if (out_nec(0x07)) /* 1st recal command */
return STA_1_NOT_READY;
if (out_nec(nec.unit))
return STA_1_NOT_READY;
LOG((log,"\n"));
seek_exec(track);
if (nec.sta.pcn) /* not yet on track 0 ? */
{
irq_flag = 0;
LOG((log,"NEC765 command "));
if (out_nec(0x07)) /* 2nd recal command */
return STA_1_NOT_READY;
if (out_nec(nec.unit))
return STA_1_NOT_READY;
LOG((log,"\n"));
seek_exec(track);
}
/* reset unit type */
unit_type[nec.unit] = 0xff;
return FDC_STA1();
}
UINT8 osd_fdc_seek(UINT8 t, UINT8 * track)
{
if (nec.sta.pcn == (t << nec.dstep))
{
*track = nec.sta.pcn;
return FDC_STA1();
}
LOG((log,"NEC765 seek unit:%d track:%02d\n", nec.unit, t));
irq_flag = 0;
LOG((log,"NEC765 command "));
if (out_nec(0x0f)) /* seek command */
return STA_1_NOT_READY;
if (out_nec(nec.unit)) /* unit number */
return STA_1_NOT_READY;
if (out_nec(t << nec.dstep)) /* track number */
return STA_1_NOT_READY;
LOG((log,"\n"));
seek_exec(track);
return FDC_STA1();
}
UINT8 osd_fdc_step(int dir, UINT8 * track)
{
int new_track;
LOG((log,"NEC765 step unit:%d direction:%+d (PCN:%02d)\n", nec.unit, dir, nec.sta.pcn));
irq_flag = 0;
LOG((log,"NEC765 command "));
if (out_nec(0x0f)) /* seek command */
return STA_1_NOT_READY;
if (out_nec(nec.unit))
return STA_1_NOT_READY;
new_track = nec.sta.pcn + (dir << nec.dstep);
if (new_track < 0)
new_track = 0;
if (new_track > 82)
new_track = 82;
if (out_nec(new_track))
return STA_1_NOT_READY;
LOG((log,"\n"));
seek_exec(track);
if (track)
*track = nec.sta.pcn;
return FDC_STA1();
}
UINT8 osd_fdc_format(UINT8 t, UINT8 h, UINT8 spt, UINT8 * fmt)
{
int i;
/* let the motors run */
osd_fdc_motors(nec.unit);
LOG((log,"NEC765 format unit:%d track:%02d head:%d sec/track:%d\n", nec.unit, t, h, spt));
#if RE_INTERLEAVE
/* reorder the sector sequence for PC optimized reading (interleave) */
{
UINT8 sec;
UINT8 sec_min = 255;
UINT8 sec_max = 0;
for (i = 0; i < spt; i++)
{
sec = fmt[i * 4 + 2];
if (sec < sec_min)
sec_min = sec;
if (sec > sec_max)
sec_max = sec;
}
/* only reorder if all sectors from min to max are in the track! */
if (sec_max - sec_min + 1 == spt)
{
LOG((log,"NEC765 reordered sectors:"));
sec = sec_min;
for (i = 0; i < spt; i++)
{
LOG((log," %3d", sec));
fmt[i * 4 + 2] = sec;
sec += spt / RE_INTERLEAVE;
if (sec > sec_max)
sec = sec - spt + 1 - spt % RE_INTERLEAVE;
}
LOG((log,"\n"));
}
}
#endif
nec.eot = 0;
for (i = 0; i < spt; i++)
{
if (fmt[i * 4 + 2] > nec.eot)
nec.eot = fmt[i * 4 + 2];
}
movedata(_my_ds(), (unsigned) fmt, nec_dma.pm_selector, nec_dma.pm_offset, spt * 4);
irq_flag = 0;
dma_write(spt * 4);
LOG((log,"NEC765 command "));
if (out_nec(nec.mfm | 0x0d)) /* format track */
return STA_2_NOT_READY;
if (out_nec((h << 2) + nec.unit)) /* head * 4 + unit select */
return STA_2_NOT_READY;
if (out_nec(nec.secl)) /* bytes per sector */
return STA_2_NOT_READY;
if (out_nec(spt)) /* sectors / track */
return STA_2_NOT_READY;
if (out_nec(nec.gap_b)) /* gap_b */
return STA_2_NOT_READY;
if (out_nec(nec.filler)) /* format filler UINT8 */
return STA_2_NOT_READY;
LOG((log,"\n"));
for (i = 0; i < 3; i++)
{
main_status();
if (!nec.stm.io_active)
break;
if (irq_flag)
break;
}
results(0);
return FDC_STA2(0);
}
UINT8 osd_fdc_put_sector(UINT8 track, UINT8 side, UINT8 head, UINT8 sector, UINT8 *buff, UINT8 ddam)
{
UINT8 result;
movedata(
_my_ds(), (unsigned) buff,
nec_dma.pm_selector, nec_dma.pm_offset,
((nec.secl) ? nec.secl << 8 : nec.dtl));
result = write_sector(track, side, head, sector, ddam);
return result;
}
UINT8 osd_fdc_get_sector(UINT8 track, UINT8 side, UINT8 head, UINT8 sector, UINT8 *buff)
{
UINT8 result;
result = read_sector(track, side, head, sector);
movedata(
nec_dma.pm_selector, nec_dma.pm_offset,
_my_ds(), (unsigned) buff,
((nec.secl) ? nec.secl << 8 : nec.dtl));
return result;
}